package gatewayapigrpcroute

/*
Copyright 2021-2025 The k8gb Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
*/

import (
	"fmt"

	context "context"

	k8gbv1beta1 "github.com/k8gb-io/k8gb/api/v1beta1"
	"github.com/k8gb-io/k8gb/controllers/logging"
	"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapi"
	"github.com/k8gb-io/k8gb/controllers/refresolver/queryopts"
	"github.com/k8gb-io/k8gb/controllers/utils"
	"k8s.io/apimachinery/pkg/api/errors"
	"sigs.k8s.io/controller-runtime/pkg/client"
	gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
)

var log = logging.Logger()

type ReferenceResolver struct {
	grpcRoute *gatewayapiv1.GRPCRoute
	gateway   *gatewayapiv1.Gateway
}

// NewReferenceResolver creates a new reference resolver capable of understanding `networking.gateway.api/v1` resources
func NewReferenceResolver(gslb *k8gbv1beta1.Gslb, k8sClient client.Client) (*ReferenceResolver, error) {
	grpcRouteList, err := getGslbGRPCRouteRef(gslb, k8sClient)
	if err != nil {
		return nil, err
	}

	if len(grpcRouteList) != 1 {
		return nil, fmt.Errorf("exactly 1 GRPCRoute resource expected but %d were found", len(grpcRouteList))
	}
	grpcRoute := grpcRouteList[0]

	route := gatewayapi.NewGRPCRouteAdapter(&grpcRoute)
	gateway, err := gatewayapi.GetGateway(route, k8sClient)
	if err != nil {
		return nil, err
	}

	return &ReferenceResolver{
		grpcRoute: &grpcRoute,
		gateway:   gateway,
	}, nil
}

// getGslbGRPCRouteRef resolves a Gateway API GRPCRoute resource referenced by the Gslb spec
func getGslbGRPCRouteRef(gslb *k8gbv1beta1.Gslb, k8sClient client.Client) ([]gatewayapiv1.GRPCRoute, error) {
	query, err := queryopts.Get(gslb.Spec.ResourceRef, gslb.Namespace)
	if err != nil {
		return nil, err
	}

	switch query.Mode {
	case queryopts.QueryModeGet:
		var grpcRoute = gatewayapiv1.GRPCRoute{}
		err = k8sClient.Get(context.TODO(), *query.GetKey, &grpcRoute)
		if err != nil {
			if errors.IsNotFound(err) {
				log.Info().
					Str("gslb", gslb.Name).
					Str("namespace", gslb.Namespace).
					Msg("Can't find referenced GRPCRoute resource")
			}
			return nil, err
		}
		return []gatewayapiv1.GRPCRoute{grpcRoute}, nil

	case queryopts.QueryModeList:
		grpcRouteList := &gatewayapiv1.GRPCRouteList{}
		err = k8sClient.List(context.TODO(), grpcRouteList, query.ListOpts...)
		if err != nil {
			if errors.IsNotFound(err) {
				log.Info().
					Str("gslb", gslb.Name).
					Str("namespace", gslb.Namespace).
					Msg("Can't find referenced GRPCRoute resource")
			}
			return nil, err
		}
		return grpcRouteList.Items, nil
	}
	return nil, fmt.Errorf("unknown query mode %v", query.Mode)
}

// GetServers retrieves the GSLB server configuration from the GRPCRoute resource
func (rr *ReferenceResolver) GetServers() ([]*k8gbv1beta1.Server, error) {
	route := gatewayapi.NewGRPCRouteAdapter(rr.grpcRoute)
	return gatewayapi.GetServersFromRoute(route)
}

// GetGslbExposedIPs retrieves the load balancer IP address of the GSLB
func (rr *ReferenceResolver) GetGslbExposedIPs(gslbAnnotations map[string]string, parentZoneDNSServers utils.DNSList) ([]string, error) {
	return gatewayapi.GetGslbExposedIPs(rr.gateway, gslbAnnotations, parentZoneDNSServers)
}
